﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.SharePoint;
using System.Web.UI.WebControls.WebParts;
using System.ComponentModel;
using Microsoft.SharePoint.WebPartPages;
using System.Web.UI;

namespace GoodStuff.SharePoint2007
{
    /// <summary>
    /// GoodStuff BaseListConnectedWebPart which extends the functionality of the BaseWebPart be providing a default SharePoint list picker.
    /// This listpicker can be used to select a (single) SPList which will be used by this WebPart to retrieve it's data.
    /// </summary>
    public abstract class BaseListConnectedWebPart : BaseWebPart, IListConnected
    {
        /// <summary>
        /// The property where the name/path of the site of the selected list is stored in.
        /// Override this property to apply your own attributes (name, description etc) 
        /// </summary>
        /// <remarks>Please note that none of these properties are not visible through the standard Sharepoint Toolparts;
        /// they are rendered through custom toolparts.</remarks>
        [Category("Eigenschappen")]
        [WebDisplayName("Naam van de site")]
        [WebDescription("Naam van de site zoals hij in Sharepoint staat")]
        [WebBrowsable(false)]
        [Personalizable(PersonalizationScope.Shared)]
        // The accessor for this property.
        public virtual string ListWebPath
        {
            get;
            set;
        }

        /// <summary>
        /// The property where the name of the selected list is stored in. Override this property to apply your own attributes (name, description etc) 
        /// </summary>
        /// <remarks>Please note that none of these properties are not visible through the standard Sharepoint Toolparts;
        /// they are rendered through custom toolparts.</remarks>
        [Category("Eigenschappen")]
        [WebDisplayName("Naam van de lijst")]
        [WebDescription("Naam van de lijst zoals hij in Sharepoint staat")]
        [WebBrowsable(false)]
        [Personalizable(PersonalizationScope.Shared)]
        // The accessor for this property.
        public virtual string ListName
        {
            get;
            set;
        }

        /// <summary>
        /// The query used for retrieval of list items. This property is null by default. In that case all listitems will be loaded
        /// by this WebPart. Override it to specify a query to retrieve certain items from the related SPlist.
        /// </summary>
        protected virtual SPQuery Query
        {
            get
            {
                return null;
            }
        }

        /// <summary>
        /// Private holder for the retrieved listitems
        /// </summary>
        private SPListItemCollection ListItems { get; set; }

        /// <summary>
        /// Before we load the usercontrol to the controlcollection, ensure we have the selected list available
        /// </summary>
        /// <param name="control"></param>
        protected override void ControlValidation(Control control)
        {
            SPList contentList = this.EnsureListAvailable();

            // Get the (optional) query. Maintain local within this method to only call the getter once, querybuilding
            // in childclass can be expensive.
            SPQuery query = this.Query;

            if (query != null)
            {
                //Retrieve items base on query.
                SPListItemCollection items = contentList.GetItems(query);

                //Sometimes the used query(syntax) isn't correct. Unfortunately SharePoint will not throw a proper
                //exception directly, but only when you do something with the retrieved items (or databind within a 
                //repeater or listview). In that latest case you'll smash your head against the wall during
                //troubleshooting. On the other hand, if we perform a .Count directly, SharePoint will throw an error
                //which we can catch. In this stadium we still have the SPQuery available, which we can add to our exception.
                try
                {
                    int aantal = items.Count;
                }
                catch (SPException spEx)
                {
                    throw new GoodStuffSharePoint2007SPQueryException(this.MessageErrorWithQuery, query, spEx);
                }

                this.ListItems = items;
            }
            else
            {
                //Retrieve all items from the selected list
                this.ListItems = contentList.Items;
            }
            
            base.ControlValidation(control);
        }

        /// <summary>
        /// After the usercontrol has been added to the controlcollection, pass the retrieved listitems to that control.
        /// </summary>
        /// <param name="control"></param>
        protected override void ControlLoaded(Control control)
        {
            if (!(control is BaseListConnectedUserControl))
            {
                throw new GoodStuffSharePoint2007Exception("Childs of a BaseListConnectedWebPart can only be used with a usercontrol that inherits from BaseListConnectedUserControl.");
            }
            BaseListConnectedUserControl baseControl = (BaseListConnectedUserControl)control;
            baseControl.HandleListItems(this.ListItems);

            base.ControlLoaded(control);
        }

        /// <summary>
        /// Ensures that a list is available. Will retrieve the SPList selected by the listpicker, or will trown an error
        /// when a list isn't selected (yet) or an invalid list is selected.
        /// </summary>
        /// <returns></returns>
        protected SPList EnsureListAvailable()
        {
            // first - ensure the is a list selected
            if (string.IsNullOrEmpty(this.ListName) || string.IsNullOrEmpty(this.ListWebPath))
            {
                throw new GoodStuffSharePoint2007Exception(this.MessageListNotSelected);
            }

            // Let see of the selected site + list are (still) available
            SPWeb web;
            try
            {
                web = SPContext.Current.Site.OpenWeb(this.ListWebPath);
            }
            catch (Exception ex)
            {
                throw new GoodStuffSharePoint2007Exception(string.Format(this.MessageSiteNotFoundFormatString, this.ListWebPath), ex);
            }

            if (web.ListExists(this.ListName) == false)
            {
                throw new GoodStuffSharePoint2007Exception(string.Format(this.MessageListNotFoundFormatString, this.ListName, this.ListWebPath));
            }

            using (web) //dispose after the return
            {
                return web.Lists[this.ListName];
            }
        }

        /// <summary>
        /// Overrides the default toolparts when HideDefaultProperties is set to true.
        /// </summary>
        /// <returns></returns>
        public override ToolPart[] GetToolParts()
        {
            if (this.HideDefaultProperties)
            {
                ToolPart[] toolparts = new ToolPart[2];

                ListpickerToolpart listpickert = new ListpickerToolpart(
                    this as IListConnected,
                    this.ListWebPath,
                    this.ListName);
                toolparts[0] = listpickert;

                CustomPropertyToolPart cptp = new CustomPropertyToolPart();
                if (!string.IsNullOrEmpty(this.ToolPaneExpandCategory))
                {
                    cptp.Expand(this.ToolPaneExpandCategory);
                }
                toolparts[1] = cptp;

                return toolparts;
            }
            else
            {
                return base.GetToolParts();
            }
        }

        /// <summary>
        /// Default message when the WebPart is loaded without a selected List. Override to specify a custom message.
        /// </summary>
        protected virtual string MessageListNotSelected
        {
            get
            {
                return "Er is voor dit WebPart nog geen lijst geselecteerd. Wijzig de eigenschappen van dit WebPart en selecteer een lijst.";
            }
        }

        /// <summary>
        /// Message when the WebPart could not found the selected site. Override to specify a custom message. 
        /// This is a formatstring, include {0} for the name/path of the site.
        /// </summary>
        protected virtual string MessageSiteNotFoundFormatString
        {
            get
            {
                return "Kan de opgegeven site {0} niet vinden. Wijzig de eigenschappen van dit WebPart en controleer de geselecteerde lijst.";
            }
        }

        /// <summary>
        /// Message when the WebPart could not found the selected list. Override to specify a custom message. 
        /// This is a formatstring, include {0} for the name of the list and {1} for the name/path of the site.
        /// </summary>
        protected virtual string MessageListNotFoundFormatString
        {
            get
            {
                return "Kan de lijst met naam {0} niet vinden in de site {1}. Wijzig de eigenschappen van dit WebPart en controleer de geselecteerde lijst.";
            }
        }

        /// <summary>
        /// Message when the WebPart could not retrieve items from the selected list based on the programmed query. Override to specify a custom message.
        /// </summary>
        protected virtual string MessageErrorWithQuery
        {
            get
            {
                return "Er is een fout opgetreden met het ophalen van lijstitems op basis van een query.";
            }
        }
    }
}
